use crate::{error::{Error, Result}};
use crate::db::model::prelude::*;
use crate::db::query_statement::prelude::*;
use crate::db::query_statement::convert_list_bind_value_to_args;
use crate::db::mysql::DbLink;
use futures_core::future::BoxFuture;
use serde::{Deserialize, Serialize};
use sqlx::mysql::{MySqlQueryResult, MySqlRow};
use sqlx::MySql;
use sqlx_mysql::MySqlArguments;
use sqlx_core::{executor, from_row::FromRow};
use std::{sync::Arc};

pub type ExceResult = Result<MySqlQueryResult>;

pub trait TableExecutor {
    type Model: ModelQuery;

    fn with() -> Self;

    fn set_db_handle(&mut self,link:Arc<DbLink>) -> &mut Self;
    fn set_sql(&mut self,sql:String) -> &mut Self;
    fn set_args(&mut self,args:MySqlArguments) -> &mut Self;
    fn set_persistent(&mut self,persistent:bool) -> &mut Self;

    fn get_db_handle(&self) -> Result<Arc<DbLink>>;
    fn get_sql(&self) -> Result<String>;
    fn get_args(&self) -> Result<MySqlArguments>;
    fn get_persistent(&self) -> Result<bool>;

    fn get_query_info(&self) -> Result<(Arc<DbLink>,String,MySqlArguments,bool)>{
        let db_handle =  self.get_db_handle()?;
        let sql =  self.get_sql()?;
        let args =  self.get_args()?;
        let persistent =  self.get_persistent()?;
        Ok((db_handle,sql,args,persistent))
    }

    fn exec(&mut self) -> BoxFuture<'static, ExceResult>{
        let run_info = self.get_query_info();
        Box::pin(async move {
            let (db_handle,sql,args,persistent) = run_info?;
            if persistent {
                db_handle.execute(sql.as_str(), args).await
            }
            else{
                db_handle.execute_no_cache(sql.as_str()).await
            }
        })
    }

    fn one(&mut self) -> BoxFuture<'static, Result<MySqlRow>>{
        let run_info = self.get_query_info();
        Box::pin(async move {
            let (db_handle,sql,args,_) = run_info?;
            db_handle.query(sql.as_str(), args).await
        })
    }

    fn one_optional(&mut self) -> BoxFuture<'static, Result<Option<MySqlRow>>>{
        let run_info = self.get_query_info();
        Box::pin(async move {
            let (db_handle,sql,args,_) = run_info?;
            db_handle.query_optional(sql.as_str(), args).await
        })
    }

    fn one_as<'a,O>(&mut self) -> BoxFuture<'static, Result<O>>
        where O: Send + Unpin + for<'r> FromRow<'r, <MySql as sqlx::Database>::Row>{
            let run_info = self.get_query_info();
            Box::pin(async move {
                let (db_handle,sql,args,_) = run_info?;
                db_handle.query_as(sql.as_str(), args).await
            })
    }

    fn one_optional_as<'a,O>(&mut self) -> BoxFuture<'static, Result<Option<O>>>
        where O: Send + Unpin + for<'r> FromRow<'r, <MySql as sqlx::Database>::Row>{
            let run_info = self.get_query_info();
            Box::pin(async move {
                let (db_handle,sql,args,_) = run_info?;
                db_handle.query_optional_as(sql.as_str(), args).await
            })
    }
    
    fn all(&mut self) -> BoxFuture<'static, Result<Vec<MySqlRow>>>{
        let run_info = self.get_query_info();
        Box::pin(async move {
            let (db_handle,sql,args,_) = run_info?;
            db_handle.query_all(sql.as_str(), args).await
        })
    }

    fn all_as<'a,O>(&mut self) -> BoxFuture<'static, Result<Vec<O>>>
        where O: Send + Unpin + for<'r> FromRow<'r, <MySql as sqlx::Database>::Row>{
        let run_info = self.get_query_info();
        Box::pin(async move {
            let (db_handle,sql,args,_) = run_info?;
            db_handle.query_all_as(sql.as_str(), args).await
        })
    }
}

pub trait Table : Sized{
    const TABLE_NAME:&'static str;
    const DATABASE_NAME:&'static str;
    type Executor: TableExecutor;
    type Model: ModelQuery;

    fn get_table_name(&self) -> Result<String>{
        Ok(format!("`{}`",Self::TABLE_NAME))
    }

    fn get_database_name(&self) -> &str{
        Self::DATABASE_NAME
    }

    fn get_sql_maker(&self) -> Result<SqlMakerRaw>;

    fn with_sql_maker(maker:SqlMakerRaw) -> Self;

    fn get_db_handle(&self) -> Result<Arc<DbLink>>{
        let db_config = self.get_database_name();
        DbLink::instance(db_config)
    }

    fn get_real_sql(sql:String,table_name:String) -> String {
        let res = sql.replace(Self::Model::get_table_tpl(), &table_name);
        res
    }

    fn build_executor(self) -> Result<Self::Executor>{
        let mut executor = Self::Executor::with();
        let db_link = self.get_db_handle();
        if db_link.is_err() {
            return Err(Error::data_is_empty("get_db_handle failed"));
        }
        let db_link = db_link.unwrap();
        let maker = self.get_sql_maker();
        if maker.is_err() {
            return Err(Error::data_is_empty("get_sql_maker failed"));
        }
        let maker = maker.unwrap();        
        let sql = Self::get_real_sql(maker.get_sql(),self.get_table_name()?);
        let args = convert_list_bind_value_to_args(maker.get_bind().unwrap_or_default());
        executor.set_db_handle(db_link).set_sql(sql).set_args(args);
        Ok(executor)
    }
}

#[derive(Debug,Clone)]
pub struct ExecutorBase{
    pub link:Option<Arc<DbLink>>,
    pub sql:Option<String>,
    pub args:Option<MySqlArguments>,
    pub persistent:bool,
}

impl Default for ExecutorBase{
    fn default() -> Self {
        Self::with()
    }
}

impl TableExecutor for ExecutorBase {
    type Model = BaseModel;

    fn with() -> Self{
        Self { link: None, sql: None, args: None, persistent: true }
    }

    fn set_db_handle(&mut self,link:Arc<DbLink>) -> &mut Self{
        self.link = Some(link);
        self
    }
    fn set_sql(&mut self,sql:String) -> &mut Self{
        self.sql = Some(sql);
        self
    }
    fn set_args(&mut self,args:MySqlArguments) -> &mut Self{
        self.args = Some(args);
        self
    }
    fn set_persistent(&mut self,persistent:bool) -> &mut Self{
        self.persistent = persistent;
        self
    }

    fn get_db_handle(&self) -> Result<Arc<DbLink>> {
        if self.link.is_none() {
            return Err(Error::data_is_empty("db link not set"))
        }
        Ok(self.link.clone().unwrap())
    }

    fn get_sql(&self) -> Result<String> {
        if self.sql.is_none() {
            return Err(Error::data_is_empty("sql not set"))
        }
        Ok(self.sql.clone().unwrap())
    }

    fn get_args(&self) -> Result<MySqlArguments> {
        if self.args.is_none() {
            return Err(Error::data_is_empty("args not set"))
        }
        Ok(self.args.clone().unwrap())
    }
    fn get_persistent(&self) -> Result<bool> {
        Ok(self.persistent)
    }
}

pub mod prelude{
    pub use super::{
        Table,
        ExceResult,
        ExecutorBase,
        TableExecutor,
    };
}

#[macro_export]
macro_rules! lavender_default_table_property {
    () => {
        type Model = $crate::db::model::BaseModel;
        type Executor = $crate::db::table::ExecutorBase;
        fn with_sql_maker(maker: $crate::db::query_statement::SqlMakerRaw) -> Self{
            Self{
                sql_maker:Some(maker)
            }
        }
        fn get_sql_maker(&self) -> $crate::error::Result<$crate::db::query_statement::SqlMakerRaw> {
            if self.sql_maker.is_none() {
                return Err($crate::error::Error::data_is_empty("sql maker not set or set error"));
            }
            Ok(self.sql_maker.clone().unwrap())
        }    
  
    };
}

// pub struct BaseTable{
//     pub sql_maker: Option<SqlMakerRaw>,
// }

// impl Table for BaseTable{
//     const TABLE_NAME:&'static str = "test";
//     const DATABASE_NAME:&'static str = "test";
//     lavender_default_table_property!();   
// }
